package jfreechart;

import java.awt.BasicStroke;
import java.awt.Color;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import log.LogUtil;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickUnit;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.experimental.chart.swt.ChartComposite;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleInsets;

import resource.ColorManager;

public class JFreeChartProxy {

	private JFreeChart jFreeChart;

	// 有多条曲线时，将曲线保存在timeSeriesMap中。此时timeSeries为null。
	private HashMap<String, TimeSeries> timeSeriesMap = new HashMap<String, TimeSeries>();

	protected ChartCompositeWithMenu chartComposite;

	private TimeSeriesCollection seriesCollection;

	private static final String DEFAULT_TITLE = "";

	private JFreeChartConfig config = new JFreeChartConfig();

	/**
	 * 构造函数。
	 * 
	 * @param builder
	 *            JFreeChartProxy.Builder对象。
	 * @param composite
	 *            父Composite。
	 */
	protected JFreeChartProxy(Builder builder, Composite composite) {
		config.setAxisColor(builder.axisColor);
		config.setAxisLabelColor(builder.axisLabelColor);
		config.setBackgroundColor(builder.backgroundColor);
		config.setGridColor(builder.gridColor);
		config.setGridVisible(builder.isGridVisible);
		config.setTitles(builder.titleList.toArray(new String[0]));
		config.setxLabel(builder.xLabel);
		config.setyLabel(builder.yLabel);
		config.setLineColor(builder.lineColor);
		config.setLineWidth(builder.lineWidth);
		config.setMaximumItemCount(builder.maximumItemCount);
		config.setHighMarkText(builder.highMarkText);
		config.setHighMarkValue(builder.highMarkValue);
		config.setLowMarkText(builder.lowMarkText);
		config.setLowMarkValue(builder.lowMarkValue);
		config.setMax(builder.max);
		config.setMin(builder.min);
		config.setLegendVisible(builder.isLegendVisible);
		config.setxDateTickUnit(builder.xDateTickUnit);
		config.setyNumberTickUnit(builder.yNumberTickUnit);
		config.setxDateFormat(builder.xDateFormat);
		config.setyNumberFormat(builder.yNumberFormat);

		this.jFreeChart = createJFreeChart(composite, config);
	}

	/**
	 * 增加一个点。
	 * 
	 * @param datetime
	 *            时间。
	 * @param value
	 *            值。
	 */
	public void add(Date datetime, double value) {
		add(DEFAULT_TITLE, datetime, value);
	}

	/**
	 * 清除默认的曲线。
	 */
	public void clear() {
		clear(DEFAULT_TITLE);
	}

	/**
	 * 清除指定名称的曲线。
	 * 
	 * @param title
	 */
	public void clear(String title) {
		try {
			if (timeSeriesMap.get(title) != null) {
				timeSeriesMap.get(title).clear();
			}
		} catch (Exception e) {
			LogUtil.error(e);
		}
	}

	/**
	 * 为指定名称的曲线，增加一个点。
	 * 
	 * @param title
	 *            曲线名称。
	 * @param datetime
	 *            时间。
	 * @param value
	 *            值。
	 */
	public void add(String title, Date datetime, double value) {
		try {
			if (timeSeriesMap.get(title) != null) {
				timeSeriesMap.get(title).addOrUpdate(new Millisecond(datetime),
						value);
			}
		} catch (Exception e) {
			LogUtil.error(e);
		}
	}

	/**
	 * 创建一条曲线。
	 * 
	 * @param title
	 *            曲线名称。
	 * @param timeSeries
	 *            各个点值。
	 */
	public void add(String title, TimeSeries timeSeries) {
		try {
			if (timeSeriesMap.get(title) != null) {
				timeSeriesMap.get(title).addAndOrUpdate(timeSeries);
			}
		} catch (Exception e) {
			LogUtil.error(e);
		}
	}

	/**
	 * 删除指定名称的曲线。
	 * 
	 * @param title
	 *            曲线名称。
	 */
	public void removeTimeSeries(String title) {
		seriesCollection.removeSeries(timeSeriesMap.get(title));
		timeSeriesMap.remove(title);
	}

	/**
	 * 删除所有的曲线。
	 */
	public void removeAllTimeSeries() {
		seriesCollection.removeAllSeries();
		timeSeriesMap.clear();
	}

	/**
	 * 获取指定名称的曲线的TimeSeries对象。
	 * 
	 * @param title
	 *            曲线名称。
	 * @return 对应的TimeSeries对象。
	 */
	public TimeSeries getTimeSeries(String title) {
		return timeSeriesMap.get(title);
	}

	/**
	 * 获取JFreeChart对象。
	 * 
	 * @return JFreeChart对象。
	 */
	public JFreeChart getJFreeChart() {
		return jFreeChart;
	}

	/**
	 * 获取ChartComposite对象。
	 * 
	 * @return ChartComposite对象。
	 */
	public ChartComposite getChartComposite() {
		return chartComposite;
	}

	/**
	 * 是否被销毁。
	 * 
	 * @return 是否被销毁。
	 */
	public boolean isDisposed() {
		return chartComposite.isDisposed();
	}

	/**
	 * 销毁。
	 */
	public void dispose() {
		chartComposite.dispose();
	}

	/**
	 * 创建一条新曲线，但暂不提供各个点的信息，可以之后再增加。
	 * 
	 * @param title
	 *            曲线名称。
	 */
	public void createNewTimeSeries(String title) {
		TimeSeries tempSeries = new TimeSeries(title);

		// 最长保留的数据长度。
		if (config.getMaximumItemCount() >= 0) {
			tempSeries.setMaximumItemCount(config.getMaximumItemCount());
		} else {
			tempSeries.setMaximumItemCount(Integer.MAX_VALUE);
		}

		timeSeriesMap.put(title, tempSeries);

		seriesCollection.addSeries(tempSeries);
	}

	/**
	 * 判断某个名称的曲线是否存在。
	 * 
	 * @param title
	 *            曲线名称。
	 * @return 是否存在。
	 */
	public boolean isTimeSeriesExist(String title) {
		return timeSeriesMap.get(title) != null;
	}

	/**
	 * 设置右键菜单。
	 * 
	 * @param menu
	 *            右键菜单。
	 */
	public void setPopupMenu(Menu menu) {
		chartComposite.setPopupMenu(menu);
	}

	/**
	 * 根据JFreeChartConfig对象创建JFreeChart对象。
	 * 
	 * @param container
	 *            父Composite。
	 * @param config
	 *            JFreeChartConfig参数配置对象。
	 * @return JFreeChart对象。
	 */
	private JFreeChart createJFreeChart(Composite container,
			JFreeChartConfig config) {
		container.setLayout(new FillLayout());

		chartComposite = new ChartCompositeWithMenu(container, 0);

		seriesCollection = new TimeSeriesCollection();

		if (config.getTitles() == null || config.getTitles().length == 0) {
			config.setTitles(new String[] { DEFAULT_TITLE });
		}

		for (String title : config.getTitles()) {

			if (title == null) {
				title = DEFAULT_TITLE;
			}

			createNewTimeSeries(title);
		}

		// 横轴刻度间隔、刻度显示方式 2011.12.1 by 陆彦良
		DateAxis dateAxis = new DateAxis(config.getxLabel());
		if (config.getxDateTickUnit() != null) {
			dateAxis.setTickUnit(config.getxDateTickUnit());
		}
		dateAxis.setAxisLineStroke(new BasicStroke(1f));

		// 横轴刻度显示方式 2011.12.1 by 陆彦良
		if (config.getxDateFormat() != null) {
			dateAxis.setDateFormatOverride(config.getxDateFormat());
		}

		// 纵轴刻度间隔，刻度显示方式 2011.12.1 by 陆彦良
		NumberAxis numberAxis = new NumberAxis(config.getyLabel());
		if (config.getyNumberTickUnit() != null) {
			numberAxis.setTickUnit(config.getyNumberTickUnit());
		}

		// 纵轴刻度显示方式。 2011.12.1 by 陆彦良
		if (config.getyNumberFormat() != null) {
			numberAxis.setNumberFormatOverride(config.getyNumberFormat());
		}

		numberAxis.setAxisLineStroke(new BasicStroke(1f));

		if (config.getMax() > config.getMin()) {
			numberAxis.setRange(config.getMin(), config.getMax());
		}

		XYLineAndShapeRenderer render = new XYLineAndShapeRenderer(true, false);

		XYPlot timePlot = new XYPlot(seriesCollection, dateAxis, numberAxis,
				render);

		JFreeChart chart = new JFreeChart(timePlot);

		// 显示图例
		chart.getLegend().setVisible(config.isLegendVisible());

		// 不显示标题
		// TextTitle title = new TextTitle("Line Chart Example");
		// Font oldFont = title.getFont();
		// Font newFont = new Font(oldFont.getName(), oldFont.getStyle(), 20);
		// title.setFont(newFont);
		// title.setPaint(Color.blue);
		// chart.setTitle(title);

		// TextTitle textTitle = new TextTitle("SUbTitle222");
		// textTitle.setPosition(RectangleEdge.LEFT);
		// textTitle.setHorizontalAlignment(HorizontalAlignment.LEFT);
		// chart.addSubtitle(textTitle);

		// 图形区域距离Compoiste的边距
		// timePlot.setAxisOffset(new RectangleInsets(3, 3, 3, 3));

		// 设置纵轴文字方向(默认0)
		timePlot.getRangeAxis().setLabelAngle(Math.PI);

		// 网格是否可见
		timePlot.setRangeGridlinesVisible(config.isGridVisible());
		timePlot.setDomainGridlinesVisible(config.isGridVisible());

		BasicStroke brokenStroke = new BasicStroke(1, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND, 8, new float[] { 3 }, 0);
		// 横向网格线的线条样式
		timePlot.setDomainGridlineStroke(brokenStroke);
		// 纵向网格线的线条样式
		timePlot.setRangeGridlineStroke(brokenStroke);

		org.eclipse.swt.graphics.Color swtGridLineColor = config.getGridColor();
		java.awt.Color awtGridLineColor = translateSWTColorToAwtColor(swtGridLineColor);
		// 横向网格线的颜色g
		timePlot.setDomainGridlinePaint(awtGridLineColor);
		// 纵向网格线的颜色
		timePlot.setRangeGridlinePaint(awtGridLineColor);

		// 曲线图的背景颜色
		org.eclipse.swt.graphics.Color swtBackgroundColor = config
				.getBackgroundColor();
		java.awt.Color awtBackgroundColor = translateSWTColorToAwtColor(swtBackgroundColor);
		chart.setBackgroundPaint(awtBackgroundColor);
		chart.setBorderPaint(awtBackgroundColor);
		timePlot.setBackgroundPaint(awtBackgroundColor);

		timePlot.setNoDataMessage("无数据");

		// 隐藏坐标区域的矩形框
		timePlot.setOutlineVisible(false);

		// 上下限等标记线
		if (config.getHighMarkText() != null) {
			ValueMarker valueMaker = new ValueMarker(config.getHighMarkValue());
			valueMaker.setLabel(config.getHighMarkText());
			valueMaker.setLabelAnchor(RectangleAnchor.RIGHT);
			valueMaker.setLabelOffset(new RectangleInsets(0, 0, 20, config
					.getHighMarkText().length() * 8));
			valueMaker.setLabelPaint(Color.red);
			valueMaker.setPaint(Color.red);
			timePlot.addRangeMarker(valueMaker);
		}

		if (config.getLowMarkText() != null) {
			ValueMarker valueMaker = new ValueMarker(config.getLowMarkValue());
			valueMaker.setLabel(config.getLowMarkText());
			valueMaker.setLabelAnchor(RectangleAnchor.RIGHT);
			valueMaker.setLabelOffset(new RectangleInsets(20, 0, 0, config
					.getLowMarkText().length() * 8));
			valueMaker.setLabelPaint(Color.red);
			valueMaker.setPaint(Color.red);
			timePlot.addRangeMarker(valueMaker);
		}

		// 绘制节点形状
		// render.setBaseShapesVisible(false);
		// render.setDrawOutlines(true); // 节点边框
		// render.setUseFillPaint(true); // 节点是否自定义填充颜色
		// render.setBaseFillPaint(Color.yellow); // 节点自定义填充颜色
		// render.setSeriesShape(0, new Ellipse2D.Double(0, 0, 0, 0)); // 无节点形状
		// render.setSeriesShape(0, new Ellipse2D.Double(-5, -5, 10, 10)); //
		// 自定义节点形状

		// 只有一条曲线时，才设置线条样式、颜色。暂不支持设置多条曲线的情况。
		if (config.getTitles().length == 1) {
			// 曲线的样式
			BasicStroke seriesStroke = new BasicStroke(config.getLineWidth(),
					BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 8, null, 0);
			render.setSeriesStroke(0, seriesStroke);

			// 曲线的颜色
			render.setSeriesPaint(0,
					translateSWTColorToAwtColor(config.getLineColor()));
		}

		// 坐标轴的颜色
		org.eclipse.swt.graphics.Color swtAxisColor = config.getAxisColor();
		java.awt.Color awtAxisColor = translateSWTColorToAwtColor(swtAxisColor);

		org.eclipse.swt.graphics.Color swtAxisLabelColor = config
				.getAxisLabelColor();
		java.awt.Color awtAxisLabelColor = translateSWTColorToAwtColor(swtAxisLabelColor);

		ValueAxis rangeAxis = timePlot.getRangeAxis();
		rangeAxis.setAxisLinePaint(awtAxisColor);
		rangeAxis.setTickLabelPaint(awtAxisColor);
		rangeAxis.setLabelPaint(awtAxisLabelColor);

		ValueAxis domainAxis = timePlot.getDomainAxis();
		domainAxis.setTickLabelPaint(awtAxisColor);
		domainAxis.setAxisLinePaint(awtAxisColor);
		domainAxis.setLabelPaint(awtAxisLabelColor);

		chartComposite.setChart(chart);

		chartComposite.setDisplayToolTips(true);
		chartComposite.setDomainZoomable(false);
		chartComposite.setRangeZoomable(false);

		return chart;
	}

	/**
	 * 将SWT的颜色转换为AWT的颜色。
	 * 
	 * @param color
	 *            SWT颜色。
	 * @return AWT颜色。
	 */
	private static java.awt.Color translateSWTColorToAwtColor(
			org.eclipse.swt.graphics.Color color) {
		RGB rgb = color.getRGB();
		return new java.awt.Color(rgb.red, rgb.green, rgb.blue);
	}

	/**
	 * JFreeChartProxy的建造者。
	 * 
	 * @author 陆彦良
	 * 
	 */
	public static class Builder {
		private List<String> titleList = new LinkedList<String>();

		private String xLabel = null;

		private String yLabel = null;

		private org.eclipse.swt.graphics.Color axisColor = ColorManager.green;

		private org.eclipse.swt.graphics.Color axisLabelColor = ColorManager.green;

		private boolean isGridVisible = true;

		private org.eclipse.swt.graphics.Color gridColor = ColorManager.gray;

		private org.eclipse.swt.graphics.Color backgroundColor = ColorManager.black;

		private org.eclipse.swt.graphics.Color lineColor = ColorManager.yellow;

		private int lineWidth = 2;

		private int maximumItemCount = -1;

		private double highMarkValue = 0;

		private String highMarkText = null;

		private double lowMarkValue = 0;

		private String lowMarkText = null;

		private double max = 0;

		private double min = 0;

		private boolean isLegendVisible = false;

		// 横轴刻度间隔及横轴单位显示方式
		private DateTickUnit xDateTickUnit;

		// 纵轴刻度间隔及横轴单位显示方式
		private NumberTickUnit yNumberTickUnit;

		// 横轴单位显示方式。 注意，该变量与xDateTickUnit只需要设一个。
		private DateFormat xDateFormat;

		// 纵轴单位显示方式。 注意，该变量与yNumberTickUnit只需要设一个。
		private NumberFormat yNumberFormat;

		public Builder setMax(double max) {
			this.max = max;
			return this;
		}

		public Builder setMin(double min) {
			this.min = min;
			return this;
		}

		public Builder setHighMarkValue(double highMarkValue) {
			this.highMarkValue = highMarkValue;
			return this;
		}

		public Builder setHighMarkText(String highMarkText) {
			this.highMarkText = highMarkText;
			return this;
		}

		public Builder setLowMarkValue(double lowMarkValue) {
			this.lowMarkValue = lowMarkValue;
			return this;
		}

		public Builder setLowMarkText(String lowMarkText) {
			this.lowMarkText = lowMarkText;
			return this;
		}

		public Builder setMaximumItemCount(int maximumItemCount) {
			this.maximumItemCount = maximumItemCount;
			return this;
		}

		public Builder setLineColor(org.eclipse.swt.graphics.Color lineColor) {
			this.lineColor = lineColor;
			return this;
		}

		public Builder setLineWidth(int lineWidth) {
			this.lineWidth = lineWidth;
			return this;
		}

		public Builder setTitle(String title) {
			if (title != null) {
				titleList.add(title);
			} else {
				throw new RuntimeException("Chart title can not be null");
			}
			return this;
		}

		public Builder setxLabel(String xLabel) {
			this.xLabel = xLabel;
			return this;
		}

		public Builder setyLabel(String yLabel) {
			this.yLabel = yLabel;
			return this;
		}

		public Builder setAxisColor(org.eclipse.swt.graphics.Color axisColor) {
			this.axisColor = axisColor;
			return this;
		}

		public Builder setAxisLabelColor(
				org.eclipse.swt.graphics.Color axisLabelColor) {
			this.axisLabelColor = axisLabelColor;
			return this;
		}

		public Builder setGridVisible(boolean isGridVisible) {
			this.isGridVisible = isGridVisible;
			return this;
		}

		public Builder setGridColor(org.eclipse.swt.graphics.Color gridColor) {
			this.gridColor = gridColor;
			return this;
		}

		public Builder setBackgroundColor(
				org.eclipse.swt.graphics.Color backgroundColor) {
			this.backgroundColor = backgroundColor;
			return this;
		}

		public Builder setLegendVisible(boolean isLegendVisible) {
			this.isLegendVisible = isLegendVisible;
			return this;
		}

		public Builder setXDateTickUnit(DateTickUnit unit) {
			this.xDateTickUnit = unit;
			return this;
		}

		public Builder setYNumberTickUnit(NumberTickUnit unit) {
			this.yNumberTickUnit = unit;
			return this;
		}

		public Builder setXDateFormat(DateFormat dateFormat) {
			this.xDateFormat = dateFormat;
			return this;
		}

		public Builder setYNumberFormat(NumberFormat numberFormat) {
			this.yNumberFormat = numberFormat;
			return this;
		}

		/**
		 * 创建JFreeChartProxy对象。
		 * 
		 * @param composite
		 *            父Composite。
		 * @return JFreeChartProxy对象。
		 */
		public JFreeChartProxy build(Composite composite) {
			return new JFreeChartProxy(this, composite);
		}
	}

	public void addMouseListener(MouseListener listener) {
		chartComposite.addSWTListener(listener);
	}

	public void addMouseListener(MouseMoveListener listener) {
		chartComposite.addSWTListener(listener);
	}
	
	public static void main(String[] args) {
		JFreeChartProxy chart;
		Display display = new Display();
		Shell shell = new Shell(display);
		shell.setSize(600, 300);
		shell.setLayout(new FillLayout());
		shell.setText("Test for jfreechart running with SWT");
		final ChartComposite frame = new ChartComposite(shell, SWT.NONE, null,
				true);
		
		chart = new JFreeChartProxy(new Builder(),frame);
		
		frame.pack();
		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
	}
}